package com.zz.client.handle;

import com.zz.client.config.ChannelCache;
import com.zz.common.config.Constants;
import com.zz.common.entity.Information;
import com.zz.common.enums.MessageType;
import com.zz.common.handle.executor.MessageExecutor;
import com.zz.common.handle.executor.MessageExecutorFactory;
import com.zz.common.protocol.TunnelProtocol;
import com.zz.common.utils.ObjectUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.internal.EmptyArrays;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * 客户端通道处理器
 * @author zz
 * @ClassName ClientChannelHandler
 */
@Slf4j
public class ClientChannelHandler extends SimpleChannelInboundHandler<TunnelProtocol> {

    private final Bootstrap bootstrap;

    private final Bootstrap localBootstrap;

    public ClientChannelHandler(Bootstrap bootstrap, Bootstrap localBootstrap) {
        super(false);
        this.bootstrap = bootstrap;
        this.localBootstrap = localBootstrap;
    }

    /**
     * 通道可写状态发生变化时触发
     * @param context
     * @throws Exception
     */
    @Override
    public void channelWritabilityChanged(ChannelHandlerContext context) throws Exception {
        Channel localChannel = context.channel().attr(Constants.Attributes.LOCAL_CHANNEL).get();
        ObjectUtil.setReadable(localChannel, context.channel().isWritable());
        super.channelWritabilityChanged(context);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception {
        log.error("客户端通道异常！error：{}", cause.toString());
        context.close();
    }

    /**
     * 处理请求
     * @param context
     * @param protocolMsg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext context, TunnelProtocol protocolMsg) throws Exception {
        MessageExecutor executor =
                MessageExecutorFactory.getMessageExecutor(MessageType.getMessageType(protocolMsg.getType()));
        if (Objects.isNull(executor)) {
            log.info("错误！找不到对应的消息处理器！messageType:{}", protocolMsg.getType());
            return;
        }
        executor.execute(new Information(context, protocolMsg, bootstrap, localBootstrap));
    }


    /**
     * 连接断开时触发
     * @param context
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext context) throws Exception {
        log.error("客户端通道断开！id:{}", context.channel().id());
        Channel localChannel = context.channel().attr(Constants.Attributes.LOCAL_CHANNEL).getAndSet(Constants.Objects.CHANNEL_NULL);
        if (ObjectUtil.isNotNull(localChannel) && localChannel.isActive()) {
            localChannel.attr(Constants.Attributes.LOCAL_CHANNEL).set(Constants.Objects.CHANNEL_NULL);
        }
        Map<String, Channel> channelCache = context.channel().attr(Constants.Attributes.CHANNEL_CACHE).getAndSet(Constants.Objects.MAP_NULL);
        if (ObjectUtil.isNull(channelCache) || !channelCache.isEmpty()) {
            return;
        }
        ObjectUtil.closeChannel(localChannel);
        channelCache.values().forEach(Channel::close);
        ChannelCache.removeSalveServerChannel(context.channel());
        ChannelCache.removeConnectNum();
    }

}
